#ifndef CROX_OBJECT_H
#define CROX_OBJECT_H

#include "common.h"
#include "value.h"
#include "chunk.h"
#include "table.h"

#define OBJ_TYPE(value) (AS_OBJ(value)->type)

#define IS_STRING(value) isObjType(value, OBJ_STRING)
#define IS_FUNCTION(value) isObjectType(value, OBJ_FUNCTION)
#define IS_NATIVE(value) isObjectType(valaue, OBJ_NATIVE)
#define IS_CLOSURE(value) isObjectType(value, OBJ_CLOSURE)
#define IS_CLASS(value) isObjectType(value, OBJ_CLASS)
#define IS_INSTANCE(value) isObjType(value, OBJ_INSTANCE)

#define AS_STRING(value) ((ObjString *)AS_OBJ(value))
#define AS_CSTRING(value) (((ObjString *)AS_OBJ(value))->str)
#define AS_FUNCTION(value) ((ObjFunction *)AS_OBJ(value))
#define AS_NATIVE(value) (((ObjNative *)AS_OBJ(value))->nativeFunc)
#define AS_CLOSURE(value) ((ObjClosure *)AS_OBJ(value))
#define AS_CLASS(value) ((ObjClass *)AS_OBJ(value))
#define AS_INSTANCE(value) ((ObjInstance *)AS_OBJ(value))

typedef enum {
    OBJ_CLASS,
    OBJ_UPVALUE,
    OBJ_CLOSURE,
    OBJ_STRING,
    OBJ_FUNCTION,
    OBJ_INSTANCE,
    OBJ_NATIVE,
} ObjType;

struct RoxObj {
    ObjType type;
    bool isMarked;
    struct RoxObj *next;
};

struct RoxObjString {
    Obj obj;
    int length;
    char* str;
    uint32_t hash;
};

typedef struct RoxObjUpvalue {
    Obj obj;
    Value* location;
    Value closed;
    struct RoxObjUpvalue* next;
} ObjUpvalue;

typedef struct {
    Obj obj;
    int arity;
    int upvalueCount;
    Chunk chunk;
    ObjString* name;
} ObjFunction;

typedef Value (*NativeFunc)(int argCount, Value* args);

typedef struct {
    Obj obj;
    NativeFunc nativeFunc;
} ObjNative;

typedef struct {
    Obj obj;
    ObjFunction *function;
    ObjUpvalue** upvalues;
    int upvalueCount;
} ObjClosure;

typedef struct {
    Obj obj;
    ObjString* name;
} ObjClass;

typedef struct {
    Obj obj;
    ObjClass* clazz;
    Table fields;
} ObjInstance;

ObjInstance* newInstance(ObjClass* clazz);

ObjClass* newClass(ObjString* name);

ObjUpvalue* newUpvalue(Value* value);

ObjClosure *newClosure(ObjFunction *function);

ObjFunction* newFunction();

ObjNative* newNative(NativeFunc func);

ObjString *takeString(char *s, int length);

ObjString* copyString(const char* s, int length);

void printObject(Value value);

static inline bool isObjType(Value value, ObjType type) {
    return IS_OBJ(value) && AS_OBJ(value)->type == type;
}

#endif //CROX_OBJECT_H
